/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.http.client;

import cn.hutool.core.text.CharSequenceUtil;
import com.iwindplus.base.domain.constant.CommonConstant.ObservationConstant;
import com.iwindplus.base.http.client.domain.property.OkHttpClientProperty;
import com.iwindplus.base.http.client.domain.property.OkHttpClientProperty.Pool;
import com.iwindplus.base.http.client.manager.OkHttpClientManager;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.okhttp3.OkHttpMetricsEventListener;
import io.micrometer.core.instrument.binder.okhttp3.OkHttpObservationInterceptor;
import io.micrometer.observation.ObservationRegistry;
import jakarta.annotation.Resource;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionPool;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;

/**
 * OkHttp配置.
 *
 * @author zengdegui
 * @since 2023/08/31
 */
@Slf4j
@Configuration
@EnableConfigurationProperties({OkHttpClientProperty.class})
@ConditionalOnProperty(prefix = "okhttp.client", name = "enabled", havingValue = "true", matchIfMissing = true)
public class OkHttpClientConfiguration {

    @Resource
    private OkHttpClientProperty property;

    @Resource
    private ObservationRegistry observationRegistry;

    @Resource
    private MeterRegistry meterRegistry;

    /**
     * 创建 ConnectionPool
     *
     * @return ConnectionPool
     */
    @Bean
    @ConditionalOnMissingBean({ConnectionPool.class})
    public ConnectionPool okHttpClientConnectionPool() {
        final Pool pool = this.property.getPool();
        ConnectionPool connectionPool = new ConnectionPool(pool.getMaxConnTotal(), pool.getConnectionKeepAlive().toSeconds(), TimeUnit.SECONDS);
        log.info("OkHttpClient ConnectionPool={}", connectionPool);
        return connectionPool;
    }

    /**
     * 创建 OkHttpClient.Builder.
     *
     * @param pool 连接池
     * @return OkHttpClient.Builder
     */
    @Bean
    public OkHttpClient.Builder okHttpClientBuilder(ConnectionPool pool) {
        List<Protocol> protocols = property.getProtocols().stream().map(Protocol::valueOf).toList();

        OkHttpClient.Builder builder = new OkHttpClient.Builder()
            .protocols(protocols)
            .connectionPool(pool)
            .connectTimeout(property.getConnectTimeout())
            .readTimeout(property.getReadTimeout())
            .writeTimeout(property.getWriteTimeout())
            .callTimeout(property.getCallTimeout())
            .retryOnConnectionFailure(property.getRetry().getEnabled())
            .followRedirects(property.getFollowRedirects());

        // 观察
        if (Boolean.TRUE.equals(this.property.getEnabledObservation())) {
            builder.addInterceptor(OkHttpObservationInterceptor.builder(observationRegistry, ObservationConstant.HTTP_OBSERVATION_NAME).build());
        }

        // 指标
        if (Boolean.TRUE.equals(this.property.getEnabledMetric())) {
            builder.eventListener(OkHttpMetricsEventListener.builder(meterRegistry, ObservationConstant.HTTP_METRIC_NAME).build());
        }

        // 代理配置
        final OkHttpClientProperty.Proxy proxy = this.property.getProxy();
        if (Boolean.TRUE.equals(proxy.getEnabled())) {
            builder.proxy(new Proxy(Proxy.Type.HTTP,
                new InetSocketAddress(proxy.getHost(), proxy.getPort())));

            String user = proxy.getUsername();
            String pwd = proxy.getPassword();
            if (CharSequenceUtil.isAllNotBlank(user, pwd)) {
                builder.proxyAuthenticator((route, resp) -> {
                    String credential = Credentials.basic(user, pwd);
                    return resp.request().newBuilder()
                        .header(HttpHeaders.PROXY_AUTHORIZATION, credential)
                        .build();
                });
            }
        }

        log.info("OkHttpClient.Builder={}", builder);
        return builder;
    }

    /**
     * 创建OkHttpClient实例.
     *
     * @param builder builder
     * @return OkHttpClient
     */
    @Bean
    @ConditionalOnMissingBean
    public OkHttpClient okHttpClient(OkHttpClient.Builder builder) {
        OkHttpClient okHttpClient = builder.build();
        log.info("OkHttpClient={}", okHttpClient);
        return okHttpClient;
    }

    /**
     * 创建 OkHttpClientManager.
     *
     * @return OkHttpClientManager
     */
    @Bean
    @ConditionalOnBean(OkHttpClient.class)
    public OkHttpClientManager okHttpClientManager() {
        OkHttpClientManager okHttpClientManager = new OkHttpClientManager();
        log.info("OkHttpClientManager={}", okHttpClientManager);
        return okHttpClientManager;
    }
}
